package com.bj58.mis.apm.server.kylin.model;

import com.bj58.mis.apm.server.kylin.common.util.KylinConfig;
import com.google.common.base.Preconditions;
import com.google.common.collect.Lists;
import com.google.common.collect.Maps;

import java.io.Serializable;
import java.util.*;

public class AggregationGroup {
    private String[] includes;
    private SelectRule selectRule;
    private long partialCubeFullMask;
    private long mandatoryColumnMask;
    private List<AggregationGroup.HierarchyMask> hierarchyMasks;
    private List<Long> joints;
    private long jointDimsMask;
    private long normalDimsMask;
    private long hierarchyDimsMask;
    private List<Long> normalDims;
    private CubeDesc cubeDesc;
    private boolean isMandatoryOnlyValid;
    private HashMap<Long, Long> dim2JointMap;

    public AggregationGroup(CubeDesc cubeDesc, Map<String, Object> group, KylinConfig cfg) {
        this.cubeDesc = cubeDesc;
        init(group);
        this.isMandatoryOnlyValid = cfg.getCubeAggrGroupIsMandatoryOnlyValid();
        if (this.includes != null && this.includes.length != 0 && this.selectRule != null) {
            this.buildPartialCubeFullMask();
            this.buildMandatoryColumnMask();
            this.buildJointColumnMask();
            this.buildJointDimsMask();
            this.buildHierarchyMasks();
            this.buildHierarchyDimsMask();
            this.buildNormalDimsMask();
        } else {
            throw new IllegalStateException("AggregationGroup incomplete");
        }
    }

    public static List<AggregationGroup> buildGroups(CubeDesc cubeDesc, KylinConfig cfg){
        List<AggregationGroup> groups = new ArrayList<>();
        List<Map<String, Object>> list = cubeDesc.getAggregation_groups();
        if(list != null && list.size() > 0) {
            list.forEach( m -> {
                groups.add(new AggregationGroup(cubeDesc, m, cfg));
            });
        }
        return groups;
    }


    private void init(Map<String, Object> group){
        Object obj = group.get("includes");
        if(obj != null){
            List<String> is = (List<String>)obj;
            this.includes = is.toArray(new String[is.size()]);
        }
        obj = group.get("select_rule");
        if(obj != null){
            Map<String, Object> rules = (Map<String, Object>)obj;
            this.selectRule = new SelectRule(rules);
        }
    }
    private void buildPartialCubeFullMask() {
        this.partialCubeFullMask = 0L;
        String[] arr$ = this.includes;
        int len$ = arr$.length;

        for(int i$ = 0; i$ < len$; ++i$) {
            String dim = arr$[i$];
            Integer index = this.cubeDesc.getColumnBitIndex(dim);
            long bit = 1L << index;
            this.partialCubeFullMask |= bit;
        }
    }
    private void buildMandatoryColumnMask() {
        this.mandatoryColumnMask = 0L;
        String[] mandatory_dims = this.selectRule.mandatoryDims;
        if (mandatory_dims != null && mandatory_dims.length != 0) {
            String[] arr$ = mandatory_dims;
            int len$ = mandatory_dims.length;

            for(int i$ = 0; i$ < len$; ++i$) {
                String dim = arr$[i$];
                Integer index = this.cubeDesc.getColumnBitIndex(dim);
                this.mandatoryColumnMask |= 1L << index;
            }

        }
    }

    private void buildJointColumnMask() {
        this.joints = Lists.newArrayList();
        this.dim2JointMap = Maps.newHashMap();
        if (this.selectRule.jointDims != null && this.selectRule.jointDims.length != 0) {
            String[][] arr$ = this.selectRule.jointDims;
            int len$ = arr$.length;

            for(int i$ = 0; i$ < len$; ++i$) {
                String[] jointDims = arr$[i$];
                if (jointDims != null && jointDims.length != 0) {
                    long joint = 0L;

                    for(int i = 0; i < jointDims.length; ++i) {
                        Integer index = this.cubeDesc.getColumnBitIndex(jointDims[i]);
                        long bit = 1L << index;
                        joint |= bit;
                    }

                    Preconditions.checkState(joint != 0L);
                    this.joints.add(joint);
                }
            }

            Iterator i$ = this.joints.iterator();

            while(i$.hasNext()) {
                long jt = (Long)i$.next();

                for(int i = 0; i < 64; ++i) {
                    if ((1L << i & jt) != 0L) {
                        this.dim2JointMap.put(1L << i, jt);
                    }
                }
            }

        }
    }

    public void buildJointDimsMask() {
        long ret = 0L;

        long x;
        for(Iterator i$ = this.joints.iterator(); i$.hasNext(); ret |= x) {
            x = (Long)i$.next();
        }

        this.jointDimsMask = ret;
    }

    private void buildHierarchyMasks() {
        this.hierarchyMasks = new ArrayList();
        if (this.selectRule.hierarchyDims != null && this.selectRule.hierarchyDims.length != 0) {
            String[][] arr$ = this.selectRule.hierarchyDims;
            int len$ = arr$.length;

            for(int i$ = 0; i$ < len$; ++i$) {
                String[] hierarchy_dims = arr$[i$];
                AggregationGroup.HierarchyMask mask = new AggregationGroup.HierarchyMask();
                if (hierarchy_dims != null && hierarchy_dims.length != 0) {
                    ArrayList<Long> allMaskList = new ArrayList();
                    ArrayList<Long> dimList = new ArrayList();

                    int i;
                    for(i = 0; i < hierarchy_dims.length; ++i) {
                        Integer index = this.cubeDesc.getColumnBitIndex(hierarchy_dims[i]);
                        long bit = 1L << index;
                        if (this.dim2JointMap.get(bit) != null) {
                            bit = (Long)this.dim2JointMap.get(bit);
                        }

                        mask.fullMask |= bit;
                        allMaskList.add(mask.fullMask);
                        dimList.add(bit);
                    }

                    Preconditions.checkState(allMaskList.size() == dimList.size());
                    mask.allMasks = new long[allMaskList.size()];
                    mask.dims = new long[dimList.size()];

                    for(i = 0; i < allMaskList.size(); ++i) {
                        mask.allMasks[i] = (Long)allMaskList.get(i);
                        mask.dims[i] = (Long)dimList.get(i);
                    }

                    this.hierarchyMasks.add(mask);
                }
            }

        }
    }

    private void buildHierarchyDimsMask() {
        long ret = 0L;

        AggregationGroup.HierarchyMask mask;
        for(Iterator i$ = this.hierarchyMasks.iterator(); i$.hasNext(); ret |= mask.fullMask) {
            mask = (AggregationGroup.HierarchyMask)i$.next();
        }

        this.hierarchyDimsMask = ret;
    }

    private void buildNormalDimsMask() {
        long leftover = this.partialCubeFullMask & ~this.mandatoryColumnMask;
        leftover &= ~this.jointDimsMask;

        AggregationGroup.HierarchyMask hierarchyMask;
        for(Iterator i$ = this.hierarchyMasks.iterator(); i$.hasNext(); leftover &= ~hierarchyMask.fullMask) {
            hierarchyMask = (AggregationGroup.HierarchyMask)i$.next();
        }

        this.normalDimsMask = leftover;
        this.normalDims = this.bits(leftover);
    }

    private List<Long> bits(long x) {
        List<Long> r = Lists.newArrayList();

        long bit;
        for(long l = x; l != 0L; l ^= bit) {
            bit = Long.lowestOneBit(l);
            r.add(bit);
        }

        return r;
    }

    public boolean isOnTree(long cuboidID) {
        if (cuboidID <= 0L) {
            return false;
        } else if ((cuboidID & ~this.partialCubeFullMask) != 0L) {
            return false;
        } else {
            return this.checkMandatoryColumns(cuboidID) && this.checkHierarchy(cuboidID) && this.checkJoint(cuboidID);
        }
    }
    private boolean checkMandatoryColumns(long cuboidID) {
        if ((cuboidID & this.mandatoryColumnMask) != this.mandatoryColumnMask) {
            return false;
        } else if (cuboidID == this.cubeDesc.getFull_mark()) {
            return true;
        } else {
            return this.isMandatoryOnlyValid || (cuboidID & ~this.mandatoryColumnMask) != 0L;
        }
    }

    private boolean checkHierarchy(long cuboidID) {
        if (this.hierarchyMasks != null && this.hierarchyMasks.size() != 0) {
            Iterator i$ = this.hierarchyMasks.iterator();

            boolean meetHierarcy;
            do {
                AggregationGroup.HierarchyMask hierarchy;
                long result;
                do {
                    if (!i$.hasNext()) {
                        return true;
                    }

                    hierarchy = (AggregationGroup.HierarchyMask)i$.next();
                    result = cuboidID & hierarchy.fullMask;
                } while(result <= 0L);

                meetHierarcy = false;
                long[] arr$ = hierarchy.allMasks;
                int len$ = arr$.length;

                for(int i = 0; i < len$; ++i) {
                    long mask = arr$[i];
                    if (result == mask) {
                        meetHierarcy = true;
                        break;
                    }
                }
            } while(meetHierarcy);

            return false;
        } else {
            return true;
        }
    }

    private boolean checkJoint(long cuboidID) {
        Iterator i$ = this.joints.iterator();

        long joint;
        long common;
        do {
            if (!i$.hasNext()) {
                return true;
            }

            joint = (Long)i$.next();
            common = cuboidID & joint;
        } while(common == 0L || common == joint);

        return false;
    }

    public String[] getIncludes() {
        return includes;
    }

    public SelectRule getSelectRule() {
        return selectRule;
    }

    public long getPartialCubeFullMask() {
        return partialCubeFullMask;
    }

    public long getMandatoryColumnMask() {
        return mandatoryColumnMask;
    }

    public List<HierarchyMask> getHierarchyMasks() {
        return hierarchyMasks;
    }

    public List<Long> getJoints() {
        return joints;
    }

    public long getJointDimsMask() {
        return jointDimsMask;
    }

    public long getNormalDimsMask() {
        return normalDimsMask;
    }

    public long getHierarchyDimsMask() {
        return hierarchyDimsMask;
    }

    public List<Long> getNormalDims() {
        return normalDims;
    }

    public CubeDesc getCubeDesc() {
        return cubeDesc;
    }

    public boolean isMandatoryOnlyValid() {
        return isMandatoryOnlyValid;
    }

    public HashMap<Long, Long> getDim2JointMap() {
        return dim2JointMap;
    }

    public int getDimCap() {
        return this.selectRule.dimCap == null ? 0 : this.selectRule.dimCap;
    }

    public static class HierarchyMask implements Serializable {
        public long fullMask;
        public long[] allMasks;
        public long[] dims;

        public HierarchyMask() {
        }
    }
}